#pragma once

#include <queue>
#include <pthread.h>
namespace blockqueue
{
    using namespace std;

    template <class T>
    class blockQueue
    {
    private:
#define MAXCAP 5 /*缓冲区上限大小，随时可变*/
    public:
        blockQueue(const size_t &cap = MAXCAP) : _cap(cap)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_productorCond, nullptr);
            pthread_cond_init(&_consumerCond, nullptr);
        }
        ~blockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_productorCond);
            pthread_cond_destroy(&_consumerCond);
        }

        /*向阻塞队列(缓冲区)生产数据*/
        void push(const T &in)
        {
            pthread_mutex_lock(&_mutex);

            /*如果阻塞队列为满，生产者不能生产，进入自己的条件变量等待*/
            while(isFull())
            {/*这里必须使用while而不是if*/
                /*进入条件变量相当于发生一次线程切换
                 *然是要将锁释放，让其他线程拥有锁
                 *这就是为什么需要传入锁的原因*/
                pthread_cond_wait(&_productorCond,&_mutex);
            }
            _q.push(in);

            /*生产者生产一个，说明阻塞队列就多一个数据
             *所以此时可以唤醒消费者消费*/
            pthread_cond_signal(&_consumerCond);
            pthread_mutex_unlock(&_mutex);
        }

        /*从阻塞队列(缓冲区)拿数据
         *使用输出型参数*/
        void pop(T *out)
        {
            pthread_mutex_lock(&_mutex);
            while(isEmpty())
            {
                pthread_cond_wait(&_consumerCond,&_mutex);
            }
            *out = _q.front();
            _q.pop();
            pthread_cond_signal(&_productorCond);
            pthread_mutex_unlock(&_mutex);
        }

    private:
        bool isFull()
        {
            return _q.size() == _cap;
        }
        bool isEmpty()
        {
            return _q.size() == 0;
        }
    private:
        /*以一个队列作为缓冲区*/
        queue<T> _q;

        /*生产者与消费者之间存在互斥与同步关系
         *故定义一把锁、生产者条件变量、消费者条件变量*/
        pthread_mutex_t _mutex;
        pthread_cond_t _productorCond;
        pthread_cond_t _consumerCond;

        /*缓冲区的大小*/
        size_t _cap;
    };
} /*namespace blockqueue ends here*/